前言
在之前项目的开发过程中,下图这样的需求很是常见:
当键盘弹起时,某个布局正好在键盘之上,当键盘消失时,这个布局又回到页面最底部。
今天来讨论的就是这个功能我用过的方法及踩过的坑。
方案1
首先我们观察到下面的一块布局(以下简称 “A” 布局)是随着键盘的弹起和消失来改变位置的,并且距离屏幕的底部正好是键盘的高度。
所以,第一个思路,设置 A 布局的位置方式为绝对定位,并且是计算距离父布局底部的位置为键盘的高度。
代码中定义一个展示键盘高度的变量 keyboardHeight ,然后设置键盘显示与消失的事件监听,并给 keyboardHeight 变量赋值:
1 | componentDidMount(){ |
然后在 render 方法中设置 A 布局的样式:
1 | render() { |
然后,我们来看一下效果:
咦,好像翻车了。
可以看到当键盘没有弹出的时候是好的,但是当键盘弹出后 A 布局就不见了。那这又有两种情况,一种是 A 布局在正确位置的上方,超出屏幕了,一种是在正确位置的下方,被键盘遮挡没弹起来。
那我们来将 A 布局的位置改为 keyboardHeight -150
看下效果:
嗯,A 布局出现了,并且是在键盘的上方。由此可见我们的第一个猜想是对的。
那我们不妨再大胆做个猜想,A 布局偏离正确位置的高度会不会就是一个键盘的高度呢?如果是这样的话,那我们 A 布局的 bottom 就应该为 keyboardHeight - keyboardHeight
即为 0!
改下代码,然后来看下效果
貌似完美! 也就是在 android 上也不需要去计算键盘的高度,布局直接在键盘之上?这个原理我还不清楚,但感觉和输入框与软键盘之间的恩怨有联系。
然后我们再看下在 ios 上的表现,嗯,这里没有测试机,就不贴图了。但事实证明会有个问题,在 ios 上如果这样设置当键盘显示的时候,A 布局不会弹起来,而是还在屏幕的底部。所以,ios 还是要计算键盘的高度。
所以,最后适配如下:
1 | <View style = {[styles.bottomView,{bottom:Platform.OS == 'ios'? this.state.keyboardHeight:0}]}> |
在这种方案实行了俩星期后,测试就提出来严重的兼容性问题:
- 在某些 android 手机上(如魅蓝),键盘弹起时,A 布局会距离键盘上方大约一个键盘的高度,即弹的太上了
- 在某些 android 手机 或者 iPhoneX 上,键盘弹起时 A 布局弹不出来
这两个问题都是偶现的,但在某些机型上的偶现率达 50%
所以,看来方案一是有问题。
但问题究竟出在哪里,我没弄明白,但敢肯定还是和输入框与软键盘的适配有关,曾经尝试设置 activity 的 softnput 参数,未果。
方案2
那我们就来换种思路,方案一我们是采用基于 bottom 的定位。但也许由于软键盘与输入框的特殊关系,这个 bottom 的值在不同机型上表现不同。那我们能否改为基于 top 定位呢。
分析一下,如果是基于 top 定位的情况,值应该怎么算,来看一下分析图:
- 当键盘没有弹起的时候,A 布局距离父布局的顶部的距离是
h- bottomHeight
,即父布局高度减去 A 布局自身高度。 当键盘弹起时,A 布局距离父布局的顶部的距离是
h- bottomHeight-keyboardHeight
,即多减掉键盘的高度。所以最终计算方式为:父布局的高度 - A 布局自身的高度 - 键盘的高度
代码中,我们首先需要计算一下父布局的高度,通过 onLayout 方法计算出。然后设置 A 布局的 top 属性值
1 | _onLayout(event) { |
刷新一下,看一下效果:
貌似没什么毛病,通过在其他机型和 ios 上测试,目前没有发现问题。
所以第二种方案可行。
总结
以后遇到这种需求,最好要避免根据 bottom 来定位,可以换种思路,根据 top 来定位。